<?php
/**
 * Created by PhpStorm.
 * User: phperstar
 * Date: 2019/12/2
 * Time: 3:30 PM
 */

namespace Util\WeiXin;

use Mall\Framework\Core\ResultWrapper;
use Mall\Framework\Core\ErrorCode;
use Mall\Framework\Factory;

class Oplatform
{
    // 第三方平台 appId
    private $appid;

    // 第三方平台 appsecret
    private $appsecret;

    // 第三方平台接收消息的校验token
    private $token;

    // 第三方平台接收消息的加密symmetric_key
    private $encodingAesKey;

    // 验证票据（component_verify_ticket）缓存
    private $component_verify_ticket = 'componentVerifyTicket';

    // 调用令牌（component_access_token）缓存
    private $component_access_token = 'componentAccessToken';

    // 预授权码（pre_auth_code）缓存
    private $pre_auth_code = 'preAuthCode';

    // 接口调用令牌（authorizer_access_token）缓存
    private $authorizer_access_token = 'authorizerAccessToken';

    // 刷新令牌 (authorizer_refresh_token) 缓存
    private $authorizer_refresh_token = 'authorizerRefreshToken';

    // 接口主域名
    private $baseUrl = 'https://api.weixin.qq.com/cgi-bin/component/';

    // 小程序
    private $wxUrl = 'https://api.weixin.qq.com/wxa/';

    // 微信认证域名
    private $wxverifyUrl = 'https://api.weixin.qq.com/cgi-bin/wxverify/';

    //微信账户
    private $wxAccount = 'https://api.weixin.qq.com/cgi-bin/account/';


    public function __construct($appid, $token, $encodingAesKey, $appsecret)
    {
        $this->appid = $appid;
        $this->token = $token;
        $this->encodingAesKey = $encodingAesKey;
        $this->appsecret = $appsecret;
    }

    /**
     * 获取平台令牌 component_access_token
     * 官方文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/component_access_token.html
     */
    public function componentAccessToken()
    {
        $authorizer_access_token = Factory::cache('default')->get($this->component_access_token . ':' . $this->appid);
        if (!empty($authorizer_access_token)) {
            return ResultWrapper::success($authorizer_access_token);
        }
        $component_verify_ticket = Factory::cache('default')->get($this->component_verify_ticket . ':' . $this->appid);
        if (empty($component_verify_ticket)) {
            return ResultWrapper::fail('component_verify_ticket验证票据为空', ErrorCode::$contentNotExists);
        }
        $postData = [
            'component_appid'         => $this->appid,
            'component_appsecret'     => $this->appsecret,
            'component_verify_ticket' => $component_verify_ticket,
        ];

        $url = $this->baseUrl . 'api_component_token';
        $response = request($url, json_encode($postData), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        Factory::cache('default')->set($this->component_access_token . ':' . $this->appid, $authorizer_access_token['component_access_token'], 7000);
        return ResultWrapper::success($authorizer_access_token['component_access_token']);
    }

    /**
     * 公共处理返回结果函数
     * https://developers.weixin.qq.com/doc/oplatform/Return_codes/Return_code_descriptions_new.html
     */
    public function commonResponse($response, $isBinary = false)
    {
        if ($response['httpcode'] != 200) {
            return ResultWrapper::fail('请求外部系统接口报错', ErrorCode::$apiNotResult);
        }

        if (!is_object($response['content']) && $isBinary) {
            return ResultWrapper::success($response['content']);
        }
        $responseData = json_decode($response['content'], true);
        if (isset($responseData['errcode']) && $responseData['errcode'] != 0) {
            return ResultWrapper::fail($responseData['errmsg'], $responseData['errcode']);
        }
        return ResultWrapper::success($responseData);
    }

    /**
     * 创建小程序接口
     * 官网文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Fast_Registration_Interface_document.html
     */
    public function fastregisterweappCreate($registerData)
    {
        $result = self::componentAccessToken();
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $component_access_token = $result->getData();

        $postData = [
            'name'                 => $registerData['name'],
            'code'                 => $registerData['code'],
            'code_type'            => $registerData['code_type'],
            'legal_persona_wechat' => $registerData['legal_persona_wechat'],
            'legal_persona_name'   => $registerData['legal_persona_name'],
            'component_phone'      => $registerData['component_phone'],
        ];

        $url = $this->baseUrl . 'fastregisterweapp?action=create&component_access_token=' . $component_access_token;
        $response = request($url, json_encode($postData, JSON_UNESCAPED_UNICODE), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }

        return ResultWrapper::success($result->getData());
    }

    /**
     * 查询创建状态
     */
    public function fastregisterweappSearch($registerData)
    {
        $result = self::componentAccessToken();
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $component_access_token = $result->getData();

        $postData = [
            'name'                 => $registerData['name'],
            'legal_persona_wechat' => $registerData['legal_persona_wechat'],
            'legal_persona_name'   => $registerData['legal_persona_name'],
        ];

        $url = $this->baseUrl . 'fastregisterweapp?action=search&component_access_token=' . $component_access_token;
        $response = request($url, json_encode($postData), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }

        return ResultWrapper::success($result->getData());
    }

    /**
     * 微信认证名称检测
     * 官网文档地址: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/wxverify_checknickname.html
     */
    public function checkwxverifynickname($authorizer_appid, $nickName)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $postData = [
            'nick_name' => $nickName,
        ];

        $url = $this->wxverifyUrl . 'checkwxverifynickname?access_token=' . $authorizer_access_token;
        $response = request($url, json_encode($postData, JSON_UNESCAPED_UNICODE), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }

    /**
     * 设置名称
     * 官网文档地址：https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/setnickname.html
     */
    public function setnickname($authorizer_appid, $nickName)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $postData = [
            'nick_name' => $nickName,
        ];

        $url = $this->wxUrl . 'setnickname?access_token=' . $authorizer_access_token;
        $response = request($url, json_encode($postData, JSON_UNESCAPED_UNICODE), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }

    /**
     * 获取预授权码 pre_auth_code
     * 官网文档地址: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/pre_auth_code.html
     */
    public function preAuthCode()
    {
        $pre_auth_code = Factory::cache('default')->get($this->pre_auth_code . ':' . $this->appid);
        if (!empty($pre_auth_code)) {
            return ResultWrapper::success($pre_auth_code);
        }

        $result = self::componentAccessToken();
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $component_access_token = $result->getData();

        $postData = [
            'component_access_token' => $component_access_token,
            'component_appid'        => $this->appid,
        ];

        $url = $this->baseUrl . 'api_create_preauthcode?component_access_token=' . $component_access_token;
        $response = request($url, json_encode($postData), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $pre_auth_code = $result->getData();
        Factory::cache('default')->set($this->pre_auth_code . ':' . $this->appid, $pre_auth_code['pre_auth_code'], 500);
        return ResultWrapper::success($pre_auth_code['pre_auth_code']);
    }

    /**
     * 获取授权信息 authorizer_refresh_token
     */
    public function apiQueryAuth($authorization_code)
    {
        $result = self::componentAccessToken();
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $component_access_token = $result->getData();


        $postData = [
            'component_access_token' => $component_access_token, // 令牌
            'component_appid'        => $this->appid, // 第三方平台appid
            'authorization_code'     => $authorization_code, // 授权码
        ];

        $url = $this->baseUrl . 'api_query_auth?component_access_token=' . $component_access_token;
        $response = request($url, json_encode($postData), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorization_info = $result->getData();
        $authorization_info = $authorization_info['authorization_info'];

        Factory::cache('default')->set($this->authorizer_access_token . ':' . $authorization_info['authorizer_appid'], $authorization_info['authorizer_access_token'], 6000);
        Factory::cache('default')->set($this->authorizer_refresh_token . ':' . $authorization_info['authorizer_appid'], $authorization_info['authorizer_refresh_token'], 7000);
        Factory::cache('default')->del($this->pre_auth_code . ':' . $this->appid);
        return ResultWrapper::success($authorization_info);

    }

    /**
     * 拉取所有已授权的帐号信息 https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/api_get_authorizer_list.html
     * @param $authorizer_appid
     * @return ResultWrapper
     * @throws \Exception
     */
    public function apiGetAuthorizerList()
    {
        $result = self::componentAccessToken();
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $component_access_token = $result->getData();

        $postData = [
            'component_access_token' => $component_access_token, // 令牌
            'component_appid'        => $this->appid, // 第三方平台appid
            'offset'                 => 0,
            'count'                  => 500,
        ];
        $url = $this->baseUrl . 'api_get_authorizer_list?component_access_token=' . $component_access_token;
        $response = request($url, json_encode($postData), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $auth_list = $result->getData();
        return ResultWrapper::success($auth_list);
    }


    /**
     * 获取/刷新接口调用令牌 authorizer_access_token
     * 官网文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/api_authorizer_token.html
     */
    public function apiAuthorizerToken($authorizer_appid, $authorizer_refresh_token = null)
    {
        $authorizer_access_token = Factory::cache('default')->get($this->authorizer_access_token . ':' . $authorizer_appid);
        if (!empty($authorizer_access_token)) {
            return ResultWrapper::success($authorizer_access_token);
        }

        $result = self::componentAccessToken();
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $component_access_token = $result->getData();

        //如果有刷新token,则使用返回的刷新token
        if (empty($authorizer_refresh_token)) {
            $authorizer_refresh_token = Factory::cache('default')->get($this->authorizer_refresh_token . ':' . $authorizer_appid);
            if (empty($authorizer_refresh_token)) {
                return ResultWrapper::fail('微信开发平台授权过期,请重新授权', ErrorCode::$notAllowAccess);
            }
        }

        $postData = [
            'component_access_token'   => $component_access_token, // 令牌
            'component_appid'          => $this->appid, // 第三方平台appid
            'authorizer_appid'         => $authorizer_appid,
            'authorizer_refresh_token' => $authorizer_refresh_token,
        ];

        $url = $this->baseUrl . 'api_authorizer_token?component_access_token=' . $component_access_token;
        $response = request($url, json_encode($postData), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        Factory::cache('default')->set($this->authorizer_access_token . ':' . $authorizer_appid, $authorizer_access_token['authorizer_access_token'], 6000);
        Factory::cache('default')->set($this->authorizer_refresh_token . ':' . $authorizer_appid, $authorizer_access_token['authorizer_refresh_token'], 7000);
        return ResultWrapper::success($authorizer_access_token['authorizer_access_token']);
    }

    /**
     * 设置服务器域名
     * 官方文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Server_Address_Configuration.html
     */
    public function modifyDomain($authorizer_appid, $requestDomain)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $postData = [
            'action'          => 'set',
            'requestdomain'   => ['https://' . $requestDomain, 'https://restapi.amap.com'],
            'wsrequestdomain' => ['wss://' . $requestDomain],
            'uploaddomain'    => [QINIU_UPLOAD],
            'downloaddomain'  => ['https://' . $requestDomain, QINIU_IMAGE, 'https://upload.' . DOMAIN],
        ];

        $url = $this->wxUrl . 'modify_domain?access_token=' . $authorizer_access_token;
        $response = request($url, json_encode($postData), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }

    /**
     * 设置业务域名
     * 官方文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/setwebviewdomain.html
     */
    public function setwebviewdomain($authorizer_appid, $webviewDomain)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $postData = [
            'action'        => 'add',
            'webviewdomain' => ['https://' . $webviewDomain],//增加业务域名
        ];

        $url = $this->wxUrl . 'setwebviewdomain?access_token=' . $authorizer_access_token;
        $response = request($url, json_encode($postData), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }

    /**
     * Doc: (des="获取小程序账号基本信息")
     * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Mini_Program_Information_Settings.html
     * User: XMing
     * Date: 2020/11/9
     * Time: 3:47 下午
     * @param $authorizer_appid
     * @return ResultWrapper
     * @deprecated 仅限api快递创建的小程序使用
     */
    public function getAccountBasicInfo($authorizer_appid)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();
        $url = $this->wxAccount . 'getaccountbasicinfo?access_token=' . $authorizer_access_token;
        $response = request($url);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }

    /**
     * Doc: (des="获取小程序账户信息")
     * https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/api_get_authorizer_info.html
     * User: XMing
     * Date: 2020/11/9
     * Time: 5:04 下午
     */
    public function apiGetAuthorizerInfo($authorizer_appid)
    {
        $result = self::componentAccessToken();
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $component_access_token = $result->getData();
        $url = $this->baseUrl . 'api_get_authorizer_info?component_access_token=' . $component_access_token;
        $postData = [
            'component_appid'        => $this->appid,
            'authorizer_appid'       => $authorizer_appid,
        ];
        $response = request($url,json_encode($postData), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }


    /**
     * 上传小程序代码
     * 官方文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/commit.html
     */
    public function commit($authorizer_appid, $template_id, $ext_json, $user_version, $user_desc)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $postData = [
            'template_id'  => $template_id,
            'ext_json'     => json_encode($ext_json),
            'user_version' => $user_version,
            'user_desc'    => $user_desc
        ];

        $url = $this->wxUrl . 'commit?access_token=' . $authorizer_access_token;
        $response = request($url, json_encode($postData), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }

    /**
     * 获取已上传的代码的页面列表
     */
    public function getPage($authorizer_appid)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $url = $this->wxUrl . 'get_page?access_token=' . $authorizer_access_token;
        $response = request($url, null, 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $page_list = $result->getData();

        return ResultWrapper::success($page_list['page_list']);
    }

    /**
     * 获取体验版二维码
     */
    public function getQrcode($authorizer_appid, $path)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $url = $this->wxUrl . 'get_qrcode?access_token=' . $authorizer_access_token . '&path=' . urlencode($path);
        $response = request($url, null, 10);
        $result = self::commonResponse($response, true);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }

    /**
     * 获取审核时可填写的类目信息
     */
    public function getCategory($authorizer_appid)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $url = $this->wxUrl . 'get_category?access_token=' . $authorizer_access_token;
        $response = request($url, null, 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $categoryData = $result->getData();
        return ResultWrapper::success($categoryData['category_list']);
    }

    /**
     * 提交审核A
     */
    public function submitAudit($authorizer_appid)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        unset($result);
        $result = self::getCategory($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $categoryData = $result->getData();

        $item_list = [
            [
                'address'      => 'pages/index/index',
                'tag'          => '商城',
                'first_class'  => $categoryData[0]['first_class'],
                'second_class' => $categoryData[0]['second_class'],
                'first_id'     => $categoryData[0]['first_id'],
                'second_id'    => $categoryData[0]['second_id'],
                'title'        => '首页',
            ]
        ];
        $postData = [
            'item_list' => $item_list,
        ];

        $url = $this->wxUrl . 'submit_audit?access_token=' . $authorizer_access_token;
        $response = request($url, json_encode($postData, JSON_UNESCAPED_UNICODE), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }

        return ResultWrapper::success($result->getData()['auditid']);
    }

    /**
     * 查询审核状态
     */
    public function getAuditstatus($authorizer_appid, $auditid)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $postData = [
            'auditid' => $auditid,
        ];

        $url = $this->wxUrl . 'get_auditstatus?access_token=' . $authorizer_access_token;
        $response = request($url, json_encode($postData, JSON_UNESCAPED_UNICODE), 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }

        return ResultWrapper::success($result->getData());
    }

    /**
     * 小程序审核撤回
     * 官网文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/undocodeaudit.html
     */
    public function undocodeaudit($authorizer_appid)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $url = $this->wxUrl . 'undocodeaudit?access_token=' . $authorizer_access_token;
        $response = request($url, null, 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }

        return ResultWrapper::success($result->getData());
    }

    /**
     * 发布已通过审核的小程序
     * 官网文档地址:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/code/release.html
     */
    public function release($authorizer_appid)
    {
        $result = self::apiAuthorizerToken($authorizer_appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        $authorizer_access_token = $result->getData();

        $url = $this->wxUrl . 'release?access_token=' . $authorizer_access_token;
        $response = request($url, '{}', 10);
        $result = self::commonResponse($response);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }

        return ResultWrapper::success($result->getData());
    }


    /**
     * 将公众平台回复用户的消息加密打包.
     * <ol>
     *    <li>对要发送的消息进行AES-CBC加密</li>
     *    <li>生成安全签名</li>
     *    <li>将消息密文和安全签名打包成xml格式</li>
     * </ol>
     *
     * @param $replyMsg string 公众平台待回复用户的消息，xml格式的字符串
     * @param $timeStamp string 时间戳，可以自己生成，也可以用URL参数的timestamp
     * @param $nonce string 随机串，可以自己生成，也可以用URL参数的nonce
     *                      当return返回0时有效
     *
     * @return ResultWrapper
     */
    public function encryptMsg($replyMsg, $timeStamp, $nonce)
    {


        //加密
        $return = self::encrypt($replyMsg, $this->appid);
        if (!$return->isSuccess()) {
            return ResultWrapper::fail($return->getData(), $return->getErrorCode());
        }
        $encrypt = $return->getData();
        if ($timeStamp == null) {
            $timeStamp = time();
        }

        //生成安全签名
        unset($return);
        $return = self::getSHA1($this->token, $timeStamp, $nonce, $encrypt);
        if (!$return->isSuccess()) {
            return ResultWrapper::fail($return->getData(), $return->getErrorCode());
        }
        $signature = $return->getData();

        //生成发送的xml
        $encryptMsg = self::generate($encrypt, $signature, $timeStamp, $nonce);
        return ResultWrapper::success($encryptMsg);
    }


    /**
     * 官方文档地址: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Message_Encryption/Message_encryption_and_decryption.html
     * 检验消息的真实性，并且获取解密后的明文.
     * <ol>
     *    <li>利用收到的密文生成安全签名，进行签名验证</li>
     *    <li>若验证通过，则提取xml中的加密消息</li>
     *    <li>对消息进行解密</li>
     * </ol>
     *
     * @param $msgSignature string 签名串，对应URL参数的msg_signature
     * @param $timestamp string 时间戳 对应URL参数的timestamp
     * @param $nonce string 随机串，对应URL参数的nonce
     * @param $postData string 密文，对应POST请求的数据
     * @param &$msg string 解密后的原文，当return返回0时有效
     *
     * @return ResultWrapper
     */
    public function decryptMsg($msgSignature, $timestamp, $nonce, $postData)
    {
        if (strlen($this->encodingAesKey) != 43) {
            return ResultWrapper::fail('无效的加密encodingAesKey', ErrorCode::$paramError);
        }


        //验证安全签名
        $return = self::getSHA1($this->token, $timestamp, $nonce, $postData['Encrypt']);
        if (!$return->isSuccess()) {
            return ResultWrapper::fail($return->getData(), $return->getErrorCode());
        }

        $signature = $return->getData();
        if ($signature != $msgSignature) {
            return ResultWrapper::fail('签名验证错误', ErrorCode::$notAllowAccess);
        }

        $result = self::decrypt($postData['Encrypt'], $this->appid);
        if (!$result->isSuccess()) {
            return ResultWrapper::fail($result->getData(), $result->getErrorCode());
        }
        return ResultWrapper::success($result->getData());
    }

    /**
     * 用SHA1算法生成安全签名
     * @param string $token 票据
     * @param string $timestamp 时间戳
     * @param string $nonce 随机字符串
     * @param string $encrypt_msg 密文消息
     */
    public function getSHA1($token, $timestamp, $nonce, $encrypt_msg)
    {
        //排序
        try {
            $array = array($encrypt_msg, $token, $timestamp, $nonce);
            sort($array, SORT_STRING);
            $str = implode($array);
            return ResultWrapper::success(sha1($str));
        } catch (\Exception $e) {
            return ResultWrapper::fail('sha加密生成签名失败', ErrorCode::$notAllowAccess);
        }
    }

    /**
     * 对解密后的明文进行补位删除
     * @param decrypted 解密后的明文
     * @return 删除填充补位后的明文
     */
    function PKCS7decode($text)
    {

        $pad = ord(substr($text, -1));
        if ($pad < 1 || $pad > 32) {
            $pad = 0;
        }
        return substr($text, 0, (strlen($text) - $pad));
    }

    /**
     * 对需要加密的明文进行填充补位
     * @param string $text 需要进行填充补位操作的明文
     * @return string 补齐明文字符串
     */
    function PKCS7encode($text)
    {
        $block_size = 32;
        $text_length = strlen($text);
        //计算需要填充的位数
        $amount_to_pad = $block_size - ($text_length % $block_size);
        if ($amount_to_pad == 0) {
            $amount_to_pad = $block_size;
        }
        //获得补位所用的字符
        $pad_chr = chr($amount_to_pad);
        $tmp = "";
        for ($index = 0; $index < $amount_to_pad; $index++) {
            $tmp .= $pad_chr;
        }
        return $text . $tmp;
    }

    /**
     * 对密文进行解密
     * @param string $encrypted 需要解密的密文
     * @return ResultWrapper 解密得到的明文
     */
    public function decrypt($encrypted, $appid)
    {

        try {
            $asekey = base64_decode($this->encodingAesKey . '=');
            $aesIV = substr($asekey, 0, 16);
            $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $asekey, OPENSSL_ZERO_PADDING, $aesIV);
        } catch (\Exception $e) {
            return ResultWrapper::fail('aes 解密失败', ErrorCode::$notAllowAccess);
        }

        try {
            //去除补位字符
            $result = self::PKCS7decode($decrypted);
            //去除16位随机字符串,网络字节序和AppId
            if (strlen($result) < 16) {
                return ResultWrapper::fail('解密出来数据长度小于16位', ErrorCode::$notAllowAccess);
            }
            $content = substr($result, 16, strlen($result));
            $len_list = unpack("N", substr($content, 0, 4));
            $xml_len = $len_list[1];
            $xml_content = substr($content, 4, $xml_len);
            $from_appid = substr($content, $xml_len + 4);
        } catch (\Exception $e) {
            return ResultWrapper::fail('解密后得到的buffer非法', ErrorCode::$notAllowAccess);
        }

        if ($from_appid != $appid) {
            return ResultWrapper::fail('appid 校验错误', ErrorCode::$notAllowAccess);
        }


        return ResultWrapper::success($xml_content);
    }

    /**
     * 对明文进行加密
     * @param string $text 需要加密的明文
     * @return ResultWrapper
     */
    public function encrypt($text, $appid)
    {
        try {
            //获得16位随机字符串，填充到明文之前
            $random = getRandomStr();
            $text = $random . pack("N", strlen($text)) . $text . $appid;
            $asekey = base64_decode($this->encodingAesKey . '=');
            $aesIV = substr($asekey, 0, 16);
            $text = self::PKCS7encode($text);
            $encrypted = openssl_encrypt($text, 'AES-256-CBC', $asekey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $aesIV);
            $encrypt_msg = base64_encode($encrypted);

            return ResultWrapper::success($encrypt_msg);
        } catch (\Exception $e) {
            return ResultWrapper::fail('aes 加密失败', ErrorCode::$notAllowAccess);
        }
    }

    /**
     * 生成xml消息
     * @param string $encrypt 加密后的消息密文
     * @param string $signature 安全签名
     * @param string $timestamp 时间戳
     * @param string $nonce 随机字符串
     */
    public function generate($encrypt, $signature, $timestamp, $nonce)
    {
        $format = "<xml>
<Encrypt><![CDATA[%s]]></Encrypt>
<MsgSignature><![CDATA[%s]]></MsgSignature>
<TimeStamp>%s</TimeStamp>
<Nonce><![CDATA[%s]]></Nonce>
</xml>";
        return sprintf($format, $encrypt, $signature, $timestamp, $nonce);
    }
}